/*
    Copyright © 2015-2019 by The qTox Project Contributors

    This file is part of qTox, a Qt-based graphical interface for Tox.

    qTox is libre software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    qTox is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with qTox.  If not, see <http://www.gnu.org/licenses/>.
*/


#pragma once

#include <tox/tox.h>

#include "toxfile.h"
#include "core/core.h"
#include "core/toxpk.h"
#include "model/status.h"
#include "signal.hpp"

#include <map>
#include <mutex>
#include <string>

#include <cstddef>
#include <cstdint>
#include <memory>

struct Tox;
class CoreFile;

using CoreFilePtr = std::unique_ptr<CoreFile>;

class CoreFile
{
public:
    void handleAvatarOffer(uint32_t friendId, uint32_t fileId, bool accept);
    static CoreFilePtr makeCoreFile(Core* core, Tox* tox);

    void sendFile(uint32_t friendId, std::string filename, std::string filePath,
                         long long filesize);
    void sendAvatarFile(uint32_t friendId, const std::vector<unsigned char>& data);
    void pauseResumeFile(uint32_t friendId, uint32_t fileId);
    void cancelFileSend(uint32_t friendId, uint32_t fileId);

    void cancelFileRecv(uint32_t friendId, uint32_t fileId);
    void rejectFileRecvRequest(uint32_t friendId, uint32_t fileId);
    void acceptFileRecvRequest(uint32_t friendId, uint32_t fileId, std::string path);

    unsigned int corefileIterationInterval();

//signals:
    sigslot::signal< ToxFile > fileSendStarted;
    sigslot::signal< ToxFile > fileReceiveRequested;
    sigslot::signal< ToxFile > fileTransferAccepted;
    sigslot::signal< ToxFile > fileTransferCancelled;
    sigslot::signal< ToxFile > fileTransferFinished;
    sigslot::signal< const std::string& > fileUploadFinished;
    sigslot::signal< const std::string&> fileDownloadFinished;
    sigslot::signal< ToxFile> fileTransferPaused;
    sigslot::signal< ToxFile> fileTransferInfo;
    sigslot::signal< ToxFile , bool > fileTransferRemotePausedUnpaused;
    sigslot::signal< ToxFile , bool > fileTransferBrokenUnbroken;
    sigslot::signal< const ToxPk& > fileNameChanged;
    sigslot::signal< uint32_t , const std::string& > fileSendFailed;

private:
    CoreFile(Core* core, Tox* tox);

    ToxFile* findFile(uint32_t friendId, uint32_t fileId);
    void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file);
    void removeFile(uint32_t friendId, uint32_t fileId);
    static constexpr uint64_t getFriendKey(uint32_t friendId, uint32_t fileId)
    {
        return (static_cast<std::uint64_t>(friendId) << 32) + fileId;
    }

    static void connectCallbacks(Tox& tox);
    static void onFileReceiveCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint32_t kind,
                                      uint64_t filesize, const uint8_t* fname, size_t fnameLen,
                                      void* vCore);
    static void onFileControlCallback(Tox* tox, uint32_t friendId, uint32_t fileId,
                                      Tox_File_Control control, void* vCore);
    static void onFileDataCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t pos,
                                   size_t length, void* vCore);
    static void onFileRecvChunkCallback(Tox* tox, uint32_t friendId, uint32_t fileId, uint64_t position,
                                        const uint8_t* data, size_t length, void* vCore);

    static std::string getCleanFileName(std::string filename);

//private slots:
    void onConnectionStatusChanged(uint32_t friendId, Status::Status state);

private:
    std::map<uint64_t, ToxFile> fileMap;
    Tox* tox;
    Core* core;
    //mutable std::mutex  coreLoopLock;
};
